/***************************************************************************
Name: JSView
Description: Helps you to view the source code of external js and css files
Author: Ron Beckman
Homepage: http://forum.softwareblaze.com

Copyright (C) 2008  Ron Beckman

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to:

Free Software Foundation, Inc.
51 Franklin Street
Fifth Floor
Boston, MA  02110-1301
USA
***************************************************************************/




window.addEventListener("load", jsviewLoad, false);

function jsviewLoad() {
	jsview.init();
}

window.addEventListener("unload", jsviewUnload, false);

function jsviewUnload() {
	jsview.exit();
}

var jsviewProgress = {

	_WPL: Components.interfaces.nsIWebProgressListener,

	getObserver: function() {
		return Components.classes["@mozilla.org/observer-service;1"].
		getService(Components.interfaces.nsIObserverService);
	},

	QueryInterface: function(aIID) {
		if (aIID.equals(Components.interfaces.nsIWebProgressListener)   ||
		aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
		aIID.equals(Components.interfaces.nsISupports))
			return this;
		throw Components.results.NS_NOINTERFACE;
	},

	onLinkIconAvailable: function(a) { },

	onLocationChange: function(aWebProgress, aRequest, aLocation) {
		var loading = false;
		var loc = (aLocation) ? aLocation.spec : null;

		if (aWebProgress) {
			try {
				aWebProgress.QueryInterface(Components.interfaces.nsIWebProgress);
				loading = aWebProgress.isLoadingDocument;
			} catch(e) { }
		}

		if (aRequest) {
			try {
				aRequest.QueryInterface(Components.interfaces.nsIRequest);
				loading = aRequest.isPending();
			} catch(e) { }
		}

		if (!loading || !loc || loc == 'about:blank') {
			jsview.load();
		}
	},

	onProgressChange: function(a,b,c,d,e,f) { },
	onSecurityChange: function(a,b,c) { },

	onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
		if (aStateFlags & this._WPL.STATE_STOP) {
			if (aStateFlags & this._WPL.STATE_IS_NETWORK) {
				if (aWebProgress.DOMWindow == content) {
					jsview.load();
				}
			}
		}
	},

	onStatusChange: function(a,b,c,d) { }
};

var jsviewPrefObserver = {

	prefs: null,

	init: function() {
		this.prefs = this.getPrefs();
		this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
		this.prefs.addObserver("", this, false);
	},

	uninit: function() {
		this.prefs.removeObserver("", this);
	},

	getPrefs: function() {
		return Components.classes["@mozilla.org/preferences-service;1"].
		getService(Components.interfaces.nsIPrefService).
		getBranch("jsview.");
	},

	observe: function(subject, topic, data) {
		if (topic != "nsPref:changed") return false;

		switch(data) {
			case "hideContext":
			   jsview.hideContext();
			   break;

			case "hideStatus":
			   jsview.hideStatus();
			   break;

			case "hideView":
			   jsview.hideView();
			   break;
		}
		return false;
	}
};

var jsview = {

	getClipboard: function() {
		return Components.classes["@mozilla.org/widget/clipboardhelper;1"].
		getService(Components.interfaces.nsIClipboardHelper);
	},

	getPrefs: function() {
		return Components.classes["@mozilla.org/preferences-service;1"].
		getService(Components.interfaces.nsIPrefService).
		getBranch("jsview.");
	},

	getCacheService: function() {
		return Components.classes["@mozilla.org/network/cache-service;1"].
		getService(Components.interfaces.nsICacheService).
		createSession("HTTP", 0, true);
	},

	getAppInfo: function() {
		return Components.classes['@mozilla.org/xre/app-info;1'].
		createInstance(Components.interfaces.nsIXULAppInfo);
	},

	getIOService: function(url) {
		return Components.classes["@mozilla.org/network/io-service;1"].
		getService(Components.interfaces.nsIIOService);
	},

	convertFileToURI: function(file) {
		return Components.classes["@mozilla.org/network/protocol;1?name=file"].
		createInstance(Components.interfaces.nsIFileProtocolHandler).
		newFileURI(file);
	},

	getTempDirectory: function() {
		return Components.classes["@mozilla.org/file/directory_service;1"].
		getService(Components.interfaces.nsIProperties).
		get("TmpD", Components.interfaces.nsIFile);
	},

	getStr: function(name) {
		return document.getElementById("jsview-bundle").getString(name);
	},

	getDOM: function() {
		var viw = document.getElementById('jsview-view');
		var btn = document.getElementById('jsview-button');
		var sts = document.getElementById('jsview-status');
		var ctx = document.getElementById('jsview-context');
		var MVPFF = document.getElementById('menu_viewPopup');
		var MVPSM = document.getElementById('menu_View_Popup');
		var CACM = document.getElementById('contentAreaContextMenu');

		return {viw:viw,btn:btn,sts:sts,ctx:ctx,MVPFF:MVPFF,MVPSM:MVPSM,CACM:CACM};
	},

	addCommas: function(numOld) {
		var numNew = parseInt(numOld);
		numNew = numNew.toString();
		var rgx = /(\d+)(\d{3})/;
		while (rgx.test(numNew)) {
			numNew = numNew.replace(rgx, '$1' + ',' + '$2');
		}
		return numNew;
	},

	init: function() {
		//add progress listener for all location changes and page loads
		getBrowser().addProgressListener(jsviewProgress);

		//add observer to monitor jsview prefs
		jsviewPrefObserver.init();

		//show-hide statusbar icon
		this.hideStatus();

		//show-hide context menu item
		this.hideContext();

		//show-hide view menu item
		this.hideView();

		//add onclick event to VPS menuitem on context menu so
		//users can right click and access jsview behavior win.
		//we have to do this dynamically instead of in the
		//overlay since Songbird does not include a VPS menuitem
		//on the context menu by default
		var vps = document.getElementById('context-viewsource');
		if (vps) {
			vps.setAttribute('onclick','if (gContextMenu) jsview.promptBehavior(event,this.parentNode,gContextMenu.target.ownerDocument.location.href);');
			vps.setAttribute('class','menuitem-iconic');
		}

		//add onclick event to VPS menuitem on view menu so users
		//can right click and access jsview behavior window
		var dom = this.getDOM();
		var brows = this.getAppInfo();
		var MVP = (brows.name == 'SeaMonkey') ? dom.MVPSM : dom.MVPFF;
		for (var i=0; i<MVP.childNodes.length; ++i) {
			if (MVP.childNodes[i].nodeName == 'menuitem' &&
			    MVP.childNodes[i].getAttribute('command') == 'View:PageSource') {
				MVP.childNodes[i].setAttribute('onclick','jsview.promptBehavior(event,this.parentNode,window.content.location.href);');
				MVP.childNodes[i].setAttribute('class','menuitem-iconic');
				MVP.childNodes[i].setAttribute('id','viewmenu-viewsource');
				break;
			}
		}

		//if about:blank is set as homepage then icons are not
		//set on load so we have to call the load method here
		if (window.content.location.href == 'about:blank') this.load();
	},

	exit: function() {
		//remove progress listener for location changes and page loads
		getBrowser().removeProgressListener(jsviewProgress);

		//remove pref observer
		jsviewPrefObserver.uninit();

		//remove temp files
		var file,uri;
		var prefs = this.getPrefs();
		if (prefs.getBoolPref('clearTemp')) {
			var tempDir = this.getTempDirectory();
			var files = tempDir.directoryEntries;
			while (files.hasMoreElements()) {
				file = files.getNext();
				file.QueryInterface(Components.interfaces.nsIFile);
				if (file.isFile()) {
					uri = this.convertFileToURI(file);
					uri.QueryInterface(Components.interfaces.nsIURL);
					if (uri.fileExtension == 'jsview' ||
					    uri.fileBaseName.substr(0,7) == 'jsview.') {
						file.remove(false);
					}
				}
			}
		}
	},

	//this method controls the jsview statusbar and toolbar icons
	load: function() {
		var dom = this.getDOM();
		var prefs = this.getPrefs();
		var status = prefs.getBoolPref('hideStatus');
		if (!dom.btn && status) return false;

		var icon,hidden;
		var js = this.getExternalJS();
		var css = this.getExternalCSS();

		if (js.length > 0 && css.length > 0) {
			icon = 'both';
			hidden = 'false';
		} else if (js.length > 0) {
			icon = 'js';
			hidden = 'false';
		} else if (css.length > 0) {
			icon = 'css';
			hidden = 'false';
		} else {
			icon = 'none';
			hidden = 'true';
		}

		var label = this.getStr('jsview.JS') +
				' (' + js.length + ') ' +
				this.getStr('jsview.CSS') +
				' (' + css.length + ')';

		if (!status && dom.sts) {
			dom.sts.setAttribute('jsview',icon);
			dom.sts.setAttribute('tooltiptext',label);
		}
		if (dom.btn) {
			dom.btn.setAttribute('jsview',icon);
			dom.btn.setAttribute('tooltiptext',label);
		}
		return false;
	},

	populateMenu: function(menu) {
		while (menu.firstChild) {
			menu.removeChild(menu.firstChild);
		}

		var uri,attrs,fname,cname,size,entry,cacheKey;
		var prefs = this.getPrefs();
		var js = this.getExternalJS();
		var css = this.getExternalCSS();
		var cache = this.getCacheService();
		var filesize = prefs.getBoolPref('fileSizeInKB');

		//get string translations
		var done = this.getStr('jsview.Done');
		var jsnf = this.getStr('jsview.JsNF');
		var cssnf = this.getStr('jsview.CssNF');
		var noname = this.getStr('jsview.NoName');
		var kb = this.getStr('jsview.KB');
		var bytes = this.getStr('jsview.Bytes');
		var jsall = this.getStr('jsview.ViewAllJs');
		var cssall = this.getStr('jsview.ViewAllCss');

		if (js.length < 1) {
			attrs = ['menuitem',
				  ['label',jsnf],
				  ['tooltiptext',jsnf],
				  ['disabled','true'],
				  ['class','menuitem-iconic jsview-none']];
			menu.appendChild(this.makeElement(attrs));
		} else {
			attrs = ['menuitem',
				  ['label',jsall],
				  ['tooltiptext',jsall],
				  ['oncommand',"jsview.viewAll('js');"],
				  ['class','menuitem-iconic jsview-js boldNormal']];
			menu.appendChild(this.makeElement(attrs));
		}

		for (var i=0; i<js.length; ++i) {
			//get filename from url
			fname = (js[i].fileName) ? js[i].fileName : noname;

			//add italic for chrome urls
			cname = (js[i].scheme == 'chrome') ? 'jsview-js jsview-italic' : 'jsview-js';

			//get filesize for url if cached
			cacheKey = js[i].spec.replace(/#.*$/,'');
			try {
				entry = cache.openCacheEntry(cacheKey, Components.interfaces.nsICache.ACCESS_READ, false);
				if (filesize) {
					size = ' (' + (entry.dataSize / 1024).toFixed(2) + ' ' + kb + ')';
				} else {
					size = ' (' + this.addCommas(entry.dataSize) + ' ' + bytes + ')';
				}
			} catch(e) {
				size = '';
			}

			attrs = ['menuitem',
				  ['label',fname + size],
				  ['value',js[i].spec],
				  ['statustext',js[i].spec],
				  ['tooltiptext',js[i].spec],
				  ['oncommand','jsview.viewSource(this.value,4);'],
				  ['onclick','jsview.promptBehavior(event,this.parentNode,this.value);'],
				  ['onmouseover','window.content.status = this.value;'],
				  ['onmouseout',"window.content.status = '" + done + "';"],
				  ['class','menuitem-iconic ' + cname]];
			menu.appendChild(this.makeElement(attrs));
		}

		menu.appendChild(this.makeElement(['menuseparator']));

		if (css.length < 1) {
			attrs = ['menuitem',
				  ['label',cssnf],
				  ['tooltiptext',cssnf],
				  ['disabled','true'],
				  ['class','menuitem-iconic jsview-none']];
			menu.appendChild(this.makeElement(attrs));
		} else {
			attrs = ['menuitem',
				  ['label',cssall],
				  ['tooltiptext',cssall],
				  ['oncommand',"jsview.viewAll('css');"],
				  ['class','menuitem-iconic jsview-css boldNormal']];
			menu.appendChild(this.makeElement(attrs));
		}

		for (var i=0; i<css.length; ++i) {
			//get filename from url
			fname = (css[i].fileName) ? css[i].fileName : noname;

			//add italic for chrome urls
			cname = (css[i].scheme == 'chrome') ? 'jsview-css jsview-italic' : 'jsview-css';

			//get filesize for url if cached
			cacheKey = css[i].spec.replace(/#.*$/,'');
			try {
				entry = cache.openCacheEntry(cacheKey, Components.interfaces.nsICache.ACCESS_READ, false);
				if (filesize) {
					size = ' (' + (entry.dataSize / 1024).toFixed(2) + ' ' + kb + ')';
				} else {
					size = ' (' + this.addCommas(entry.dataSize) + ' ' + bytes + ')';
				}
			} catch(e) {
				size = '';
			}

			attrs = ['menuitem',
				  ['label',fname + size],
				  ['value',css[i].spec],
				  ['statustext',css[i].spec],
				  ['tooltiptext',css[i].spec],
				  ['oncommand','jsview.viewSource(this.value,4);'],
				  ['onclick','jsview.promptBehavior(event,this.parentNode,this.value);'],
				  ['onmouseover','window.content.status = this.value;'],
				  ['onmouseout',"window.content.status = '" + done + "';"],
				  ['class','menuitem-iconic ' + cname]];
			menu.appendChild(this.makeElement(attrs));
		}
	},

	makeElement: function(attrs) {
		var elem = document.createElement(attrs[0]);
		for (var i=1; i<attrs.length; ++i) {
			elem.setAttribute(attrs[i][0],attrs[i][1]);
		}
		return elem;
	},

	viewAll: function(type) {
		var prefs = this.getPrefs();
		var bhvr = prefs.getIntPref('menuItemsBehavior');

		var uris = (type == 'js') ? this.getExternalJS() : this.getExternalCSS();

		for (var i=0; i<uris.length; ++i) {
			setTimeout("jsview.viewSource('"+uris[i].spec+"','"+bhvr+"')",i*500);
		}
	},

	viewSource: function(url,type) {
		//type: 0 = new win, 1 = new tab blur, 2 = new tab focus, 3 = external editor
		//type: 4 = menuItemsBehavior preference

		if (type == 4) {
			var prefs = this.getPrefs();
			type = prefs.getIntPref('menuItemsBehavior');
		}

		if (type == 0) {
			var brows = this.getAppInfo();
			if (brows.name == 'SeaMonkey') {
				this.viewSourceSM(url);
			} else {
				this.viewSourceFF(url);
			}
		} else if (type == 1) {
			url = 'view-source:' + url;
			getBrowser().addTab(url);
		} else if (type == 2) {
			url = 'view-source:' + url;
			var newTab = getBrowser().addTab(url);
			getBrowser().selectedTab = newTab;
		} else if (type == 3) {
			jsviewExternal.openInExternalEditor(url,null,null);
		}
	},

	copyLink: function(url) {
		var clip = this.getClipboard();
		clip.copyString(url);
	},

	getFrames: function() {
		var frms = [window.content];

		for (var i=0; i<frms.length; ++i) {
			for (var j=0; j<frms[i].frames.length; ++j) {
				frms.push(frms[i].frames[j]);
			}
			//we break after reaching 50 to avoid a possible endless loop from
			//occurring if the website has coded frames within frames.........
			if (i >= 50) break;
		}
		return frms;
	},

	getExternalJS: function() {
		var scriptUrls = [];
		var prefs = this.getPrefs();
		var io = this.getIOService();
		var scripts,loc,source,srcAttr,uri;

		//get a reference to all dom windows in current page
		var wins = this.getFrames();

		for (var i=0; i<wins.length; ++i) {
			scripts = wins[i].document.getElementsByTagName('script');
			for (var j=0; j<scripts.length; ++j) {
				if (scripts[j].hasAttribute('src')) {
					//use getAttribute for xul script elements
					//use .src for html script elements
					srcAttr = scripts[j].getAttribute('src');
					source = (srcAttr.substr(0,9) == 'chrome://') ? srcAttr : scripts[j].src;
					if (source) {
						//we need to make sure all anchors are removed from the urls
						scriptUrls.push(source.replace(/#.*$/,''));
					}
				}
			}
		}

		//we sort the urls to ensure that chrome urls appear at top of menupopup
		scriptUrls.sort();

		//check for duplicate entries and remove them
		scriptUrls = this.removeDuplicates(scriptUrls);

		//we create the uris here instead of the previous loop since the urls
		//need to be sorted and duplicates removed before the uris can be created
		for (var i=0; i<scriptUrls.length; ++i) {
			try {
				uri = io.newURI(scriptUrls[i],null,null);
				uri.QueryInterface(Components.interfaces.nsIURL);
				scriptUrls[i] = uri;
			} catch(e) {
				scriptUrls.splice(i,1);
				--i;
			}
		}

		//filter the scriptUrls array for unwanted file types
		var exts = prefs.getCharPref('unwantedFileTypes');
		if (exts) {
			scriptUrls = scriptUrls.filter(function(value) {
				return !value.fileExtension || exts.indexOf(' '+value.fileExtension+' ') == -1;
			});
		}

		//filter the scriptUrls array for any urls that match window locations
		for (var i=0; i<wins.length; ++i) {
			loc = wins[i].location.href.replace(/#.*$/,'');
			scriptUrls = scriptUrls.filter(function(value) { return value.spec != loc; });
		}

		//filter the scriptUrls array for chrome urls
		if (!prefs.getBoolPref('showChromeUrls')) {
			scriptUrls = scriptUrls.filter(function(value) { return value.scheme != 'chrome'; });
		}

		return scriptUrls;
	},

	getExternalCSS: function() {
		var sheets,loc,uri;
		var styleUrls = [];
		var prefs = this.getPrefs();
		var io = this.getIOService();

		//get a reference to all dom windows in current page
		var wins = this.getFrames();

		for (var i=0; i<wins.length; ++i) {
			sheets = wins[i].document.styleSheets;
			for (var j=0; j<sheets.length; ++j) {
				if (sheets[j].ownerNode &&
				    sheets[j].ownerNode instanceof Components.interfaces.nsIDOMHTMLStyleElement) {
					//do nothing for embedded stylesheets
				} else if (sheets[j].href) {
					//we need to make sure all anchors are removed from the urls
					styleUrls.push(sheets[j].href.replace(/#.*$/,''));
				}
				styleUrls = styleUrls.concat(this.getImportedCSS(sheets[j]));
			}
		}

		//we sort the urls to ensure that chrome urls appear at top of menupopup
		styleUrls.sort();

		//check for duplicate entries and remove them
		styleUrls = this.removeDuplicates(styleUrls);

		//we create the uris here instead of the previous loop since the urls
		//need to be sorted and duplicates removed before the uris can be created
		for (var i=0; i<styleUrls.length; ++i) {
			try {
				uri = io.newURI(styleUrls[i],null,null);
				uri.QueryInterface(Components.interfaces.nsIURL);
				styleUrls[i] = uri;
			} catch(e) {
				styleUrls.splice(i,1);
				--i;
			}
		}

		//filter the styleUrls array for unwanted file types
		var exts = prefs.getCharPref('unwantedFileTypes');
		if (exts) {
			styleUrls = styleUrls.filter(function(value) {
				return !value.fileExtension || exts.indexOf(' '+value.fileExtension+' ') == -1;
			});
		}

		//filter the styleUrls array for any urls that match window locations
		for (var i=0; i<wins.length; ++i) {
			loc = wins[i].location.href.replace(/#.*$/,'');
			styleUrls = styleUrls.filter(function(value) { return value.spec != loc; });
		}

		//filter the styleUrls array for chrome urls
		if (!prefs.getBoolPref('showChromeUrls')) {
			styleUrls = styleUrls.filter(function(value) { return value.scheme != 'chrome'; });
		}

		return styleUrls;
	},

	getImportedCSS: function(css) {
		var urls = [];
		var sheets = [css];

		for (var i=0; i<sheets.length; ++i) {
			for (var j=0; j<sheets[i].cssRules.length; ++j) {
				if (sheets[i].cssRules[j].type != 3) break;

				if (sheets[i].cssRules[j].styleSheet &&
				    sheets[i].cssRules[j].styleSheet.href) {
					//we need to make sure all anchors are removed from the urls
					urls.push(sheets[i].cssRules[j].styleSheet.href.replace(/#.*$/,''));
					sheets.push(sheets[i].cssRules[j].styleSheet);
				}
			}
			//we break after reaching 50 to avoid a possible endless loop from
			//occurring if a stylesheet tries to import itself over and over...
			if (i >= 50) break;
		}
		return urls;
	},

	removeDuplicates: function(arrOld) {
		var arrNew = [];
		for (var i=0; i<arrOld.length; ++i) {
			if (arrNew.indexOf(arrOld[i]) == -1) {
				arrNew.push(arrOld[i]);
			}
		}
		return arrNew;
	},

	hideStatus: function() {
		var dom = this.getDOM();
		if (!dom.sts) return false;

		var prefs = this.getPrefs();
		dom.sts.setAttribute('hidden',prefs.getBoolPref('hideStatus'));
		return false;
	},

	hideView: function() {
		var dom = this.getDOM();
		if (!dom.viw) return false;

		var brows = this.getAppInfo();
		var MVP = (brows.name == 'SeaMonkey') ? dom.MVPSM : dom.MVPFF;
		if (!MVP) return false;

		var prefs = this.getPrefs();
		var hv = prefs.getBoolPref('hideView');
		dom.viw.setAttribute('hidden',hv);

		if (hv) {
			MVP.removeEventListener('popupshowing', jsview.showViewPopup, false);
		} else {
			MVP.addEventListener('popupshowing', jsview.showViewPopup, false);
		}
		return false;
	},

	showViewPopup: function() {
		var dom = jsview.getDOM();
		if (!dom.viw) return false;

		var icon,hidden;
		var js = jsview.getExternalJS();
		var css = jsview.getExternalCSS();

		if (js.length > 0 && css.length > 0) {
			icon = 'both';
			hidden = 'false';
		} else if (js.length > 0) {
			icon = 'js';
			hidden = 'false';
		} else if (css.length > 0) {
			icon = 'css';
			hidden = 'false';
		} else {
			icon = 'none';
			hidden = 'true';
		}

		var label = jsview.getStr('jsview.JS') +
				' (' + js.length + ') ' +
				jsview.getStr('jsview.CSS') +
				' (' + css.length + ')';

		dom.viw.setAttribute('jsview',icon);
		dom.viw.setAttribute('hidden',hidden);
		dom.viw.setAttribute('tooltiptext',label);
		dom.viw.setAttribute('label',label);
		return false;
	},

	hideContext: function() {
		var dom = this.getDOM();
		if (!dom.ctx || !dom.CACM) return false;

		var prefs = this.getPrefs();
		var hc = prefs.getBoolPref('hideContext');
		dom.ctx.setAttribute('hidden',hc);

		if (hc) {
			dom.CACM.removeEventListener('popupshowing', jsview.showContextPopup, false);
		} else {
			dom.CACM.addEventListener('popupshowing', jsview.showContextPopup, false);
		}

		return false;
	},

	showContextPopup: function() {
		var dom = jsview.getDOM();
		if (!dom.ctx) return false;

		if ('gContextMenu' in window == false || !gContextMenu) {
			dom.ctx.setAttribute('hidden',true);
			return false;
		}

		var gCM = gContextMenu.inDirList ||
				gContextMenu.onImage ||
				gContextMenu.isContentSelected ||
				gContextMenu.onLink ||
				gContextMenu.onTextInput;

		var icon,hidden;
		var js = jsview.getExternalJS();
		var css = jsview.getExternalCSS();

		if (js.length > 0 && css.length > 0) {
			icon = 'both';
			hidden = 'false';
		} else if (js.length > 0) {
			icon = 'js';
			hidden = 'false';
		} else if (css.length > 0) {
			icon = 'css';
			hidden = 'false';
		} else {
			icon = 'none';
			hidden = 'true';
		}

		var label = jsview.getStr('jsview.JS') +
				' (' + js.length + ') ' +
				jsview.getStr('jsview.CSS') +
				' (' + css.length + ')';

		dom.ctx.setAttribute('jsview',icon);
		dom.ctx.setAttribute('hidden',(gCM) ? true : hidden);
		dom.ctx.setAttribute('tooltiptext',label);
		dom.ctx.setAttribute('label',label);

		return false;
	},

	promptBehavior: function(ev,menu,url) {
		if (ev.which > 1) {
			ev.preventDefault();
			menu.hidePopup();
			window.openDialog("chrome://jsview/content/jsviewBehavior.xul", "_blank", "chrome,centerscreen,resizable=yes", url);
		}
	},

	viewAbout: function() {
		window.openDialog("chrome://jsview/content/jsviewAbout.xul", "_blank", "chrome,centerscreen,resizable=yes", null);
	},

	viewOptions: function() {
		window.openDialog("chrome://jsview/content/jsviewOptions.xul", "_blank", "chrome,centerscreen,resizable=yes", null);
	},

	viewObjRef: function() {
		window.openDialog("chrome://jsview/content/jsviewObjRef.xul", "_blank", "chrome,centerscreen,resizable=yes", null);
	},

	viewSourceFF: function(url) {
		window.openDialog("chrome://global/content/viewSource.xul", "_blank", "all,dialog=no", url, null, null); //, null, null);
	},

	viewSourceSM: function(url) {
		window.openDialog("chrome://navigator/content/viewSource.xul", "_blank", "all,dialog=no", url, null, null); //, null, null);
	}
};
